package com.maxd.service.partner;

import com.google.common.collect.Lists;
import com.maxd.adapter.LoginUserHolder;
import com.maxd.bean.PageRes;
import com.maxd.bean.order.PartnerOrderDetail;
import com.maxd.bean.order.PartnerOrderUser;
import com.maxd.bean.partner.AddPartnerReq;
import com.maxd.bean.user.AdjustPromotePointReq;
import com.maxd.component.sequence.SeqTools;
import com.maxd.constants.PPointScene;
import com.maxd.error.ExceptionEnum;
import com.maxd.model.*;
import com.maxd.respository.OrderPartnerInfoRepository;
import com.maxd.respository.PromotePointRecordRepository;
import com.maxd.respository.selfGoods.GoodsJpaRepository;
import com.maxd.respository.selfOrder.OrdersJpaRepository;
import com.maxd.respository.selfOrder.OrdersRepository;
import com.maxd.respository.userinfo.UserJpaRepository;
import com.maxd.respository.userinfo.UserRepository;
import com.maxd.service.userinfo.IUserService;
import com.maxd.utils.AdminUtils;
import com.maxd.utils.AssertUtil;
import com.maxd.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Slf4j
public class PartnerService implements IPartnerService {
    @Autowired
    private OrdersRepository ordersRepository;

    @Autowired
    private OrderPartnerInfoRepository orderPartnerInfoRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private UserJpaRepository userJpaRepository;

    @Autowired
    private PromotePointRecordRepository promotePointRecordRepository;

    @Autowired
    private OrdersJpaRepository ordersJpaRepository;

    @Autowired
    private GoodsJpaRepository goodsJpaRepository;

    @Autowired
    private IUserService userService;

    @Value("${classes.partner}")
    private String partnerTypeId;

    @Value("${partner.group.reward-points}")
    private String rewardPoints;

    @Override
    public PageRes<PartnerOrderUser> queryList(Integer pageNo, Integer pageSize, Long userId, Long partnerCode, String userName, String telephone, Long partnerUserId, Long partnerTypeId) {
        // 分页参数
        Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
        Pageable pageable = PageRequest.of(pageNo, pageSize, sort);

        Specification<OrderPartnerInfo> queryCondition = buildCondition(userId, partnerCode, userName, telephone, partnerUserId, partnerTypeId);
        Page<OrderPartnerInfo> pageList = orderPartnerInfoRepository.findAll(queryCondition, pageable);
        log.info("分页查询到合伙人信息为:{}", JsonUtils.toJsonString(pageList));

        List<OrderPartnerInfo> records = pageList.getContent();
        List<PartnerOrderUser> partnerOrderUsers = Lists.newArrayList();
        if(!CollectionUtils.isEmpty(records)) {
            Set<Long> userIdSet = records.stream().map(OrderPartnerInfo::getPartnerUserId).collect(Collectors.toSet());
            List<UserInfo> userList = userJpaRepository.findAllById(userIdSet);
            Map<Long, UserInfo> userMap = userList.stream().collect(Collectors.toMap(UserInfo::getId, e -> e, (e1, e2) -> e2));
            for (OrderPartnerInfo record : records) {
                PartnerOrderUser orderUser = new PartnerOrderUser();
                BeanUtils.copyProperties(record, orderUser);
                orderUser.setUserInfo(userMap.get(record.getPartnerUserId()));
                partnerOrderUsers.add(orderUser);
            }
        }
        return new PageRes<>(pageList.getTotalElements(), partnerOrderUsers);
    }

    @Override
    public List<OrderPartnerInfo> queryList(Long userId, Long partnerCode, String userName, String telephone, Long partnerUserId, Long partnerTypeId) {
        Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
        Specification<OrderPartnerInfo> queryCondition = buildCondition(userId, partnerCode, userName, telephone, partnerUserId, partnerTypeId);
        List<OrderPartnerInfo> partenerList = orderPartnerInfoRepository.findAll(queryCondition, sort);
        log.info("查询到合伙人信息为:{}", JsonUtils.toJsonString(partenerList));
        return partenerList;
    }

    @Override
    public Integer updateStatus(String orderId, String status) {
        return orderPartnerInfoRepository.updateStatus(orderId, status);
    }

    @Override
    public List<OrderPartnerInfo> queryUserPartnerList(Long partnerUserId, Long partnerTypeId) {
        return queryList(null, null, null, null, partnerUserId, partnerTypeId);
    }

    @Override
    public List<OrderPartnerInfo> queryUserPartnerOrders(Long userId, Long partnerTypeId) {
        return queryList(userId, null, null, null, null, partnerTypeId);
    }

    @Override
    public List<PartnerOrderDetail> queryUserPartnerDetails(Long userId, Long partnerTypeId) {
        List<OrderPartnerInfo> partnerInfos = queryList(userId, null, null, null, null, partnerTypeId);

        List<PartnerOrderDetail> detailList = Lists.newArrayList();
        if(!CollectionUtils.isEmpty(partnerInfos)) {
            Set<String> orderIdSet = partnerInfos.stream().map(OrderPartnerInfo::getOrderId).collect(Collectors.toSet());
            Specification<Orders> queryCondition = (root, query, criteriaBuilder) -> criteriaBuilder.and(criteriaBuilder.in(root.get("orderNum")).value(orderIdSet));
            List<Orders> orderList = ordersJpaRepository.findAll(queryCondition);
            Map<String, Orders> ordersMap = orderList.stream().collect(Collectors.toMap(Orders::getOrderNum, e -> e, (e1, e2) -> e2));
            for (OrderPartnerInfo partnerInfo : partnerInfos) {
                PartnerOrderDetail partnerOrderDetail = new PartnerOrderDetail();
                BeanUtils.copyProperties(partnerInfo, partnerOrderDetail);
                partnerOrderDetail.setOrderInfo(ordersMap.get(partnerInfo.getOrderId()));
                detailList.add(partnerOrderDetail);
            }
        }
        return detailList;
    }

    @Override
    public List<OrderPartnerInfo> queryWaitPayOrders(Long userId, Long partnerTypeId) {
        Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
        Specification<OrderPartnerInfo> queryCondition = buildCondition(userId, null, null, null, null, partnerTypeId, "0");
        List<OrderPartnerInfo> partenerList = orderPartnerInfoRepository.findAll(queryCondition, sort);
        log.info("查询到合伙人信息为:{}", JsonUtils.toJsonString(partenerList));
        return partenerList;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pointUpdateProcess(Orders orders) {
        // 1. 查询合伙人订单记录
        OrderPartnerInfo partnerInfo = orderPartnerInfoRepository.queryByOrderId(orders.getOrderNum());
        log.info("合伙人购买更新积分, 订单合伙人信息:{}", JsonUtils.toJsonString(partnerInfo));
        if(partnerInfo == null) {
            log.error("合伙人购买更新积分失败，找不到合伙人订单记录.");
            return;
        }
        if(partnerInfo.getPrice() == null || partnerInfo.getDeductPercent() == null) {
            log.error("合伙人购买更新积分失败，抵扣比例缺失.");
            return;
        }
        UserInfo partnerUserInfo = userRepository.getUserById(partnerInfo.getPartnerUserId());
        UserInfo orderUserInfo = userRepository.getUserById(partnerInfo.getUserId());
        if(partnerUserInfo == null || orderUserInfo == null) {
            log.error("合伙人购买更新积分失败，用户信息不存在.");
            return;
        }
        // 2.给合伙人累计积分
        insertRecord(partnerInfo.getPartnerUserId(), "GEN", PPointScene.GEN_BE_PARTNER, partnerInfo.getPrice(), partnerInfo);
        userRepository.addPromotePoint(partnerInfo.getPrice(), partnerInfo.getPartnerUserId());
        // 3.下单人消耗积分
        if(partnerInfo.getDeductPoint() != null && partnerInfo.getDeductPoint().compareTo(BigDecimal.ZERO) > 0 ) {
            log.info("可抵积分大于实际抵扣积分，下单人消耗积分.");
            insertRecord(partnerInfo.getUserId(), "USE", PPointScene.USE_BUY_PARTNER, partnerInfo.getDeductPoint(), partnerInfo);
            userRepository.subPromotePoint(partnerInfo.getDeductPoint(), partnerInfo.getUserId());
        }
        // 4.当可抵积分 > 实际抵扣积分的情况，下单人累计积分
        BigDecimal canDeductPoint = partnerInfo.getPrice().multiply(partnerInfo.getDeductPercent());
        if(canDeductPoint.compareTo(partnerInfo.getDeductPoint()) > 0) {
            BigDecimal genPoint = canDeductPoint.subtract(partnerInfo.getDeductPoint()).setScale(0, RoundingMode.DOWN);
            if(genPoint.compareTo(BigDecimal.ZERO) > 0) {
                log.info("可抵积分大于实际抵扣积分，给下单人增加积分.");
                insertRecord(partnerInfo.getUserId(), "GEN", PPointScene.GEN_BUY_PARTNER, genPoint, partnerInfo);
                userRepository.addPromotePoint(genPoint, partnerInfo.getUserId());
            }
        }
        // 5.下单人的上级(如有)，赠送100积分
        List<OrderPartnerInfo> partnerInfos = queryUserPartnerList(partnerInfo.getUserId(), Long.valueOf(partnerTypeId));
        if(!CollectionUtils.isEmpty(partnerInfos)) {
            OrderPartnerInfo orderPartnerInfo = partnerInfos.get(partnerInfos.size() - 1);
            log.info("下单人的上级获取积分，配置的奖励积分数为:{}", rewardPoints);
            insertRecord(orderPartnerInfo.getUserId(), "GEN", PPointScene.GEN_BUY_GROUP, new BigDecimal(rewardPoints), partnerInfo);
            userRepository.addPromotePoint(new BigDecimal(rewardPoints), orderPartnerInfo.getUserId());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addPartner(AddPartnerReq req) {
        // 1. 查询商品信息
        Goods g = goodsJpaRepository.findById(req.getPartnerCode()).orElse(null);
        AssertUtil.assertTrue(g != null && g.getStatus() == 1, ExceptionEnum.PARTNER_NOF);
        AssertUtil.assertTrue(partnerTypeId.equals(g.getTypeId()), ExceptionEnum.PARTNER_NOF);
        // 2. 通过手机号查询用户信息
        UserInfo userInfo = userJpaRepository.findById(req.getUserId()).orElse(null);
        AssertUtil.notNull(userInfo, ExceptionEnum.USER_NOT_FIND);
        // 3. 插入一条记录到合伙人订单表
        insertPartnerInfo(req, g, userInfo);
        // 4. 给用户添加积分
        if(req.getAddPoint() != null && req.getAddPoint() > 0) {
            AdjustPromotePointReq adjustPromotePointReq = new AdjustPromotePointReq();
            adjustPromotePointReq.setUserId(userInfo.getId());
            adjustPromotePointReq.setNumber(req.getAddPoint());
            adjustPromotePointReq.setOperation("GEN");
            adjustPromotePointReq.setRemark("新增合伙人增加积分.");
            userService.adjustPromotePoint(adjustPromotePointReq);
        }
    }

    private void insertPartnerInfo(AddPartnerReq req, Goods goods, UserInfo partnerUser) {
        OrderPartnerInfo partnerInfo = new OrderPartnerInfo();
        partnerInfo.setOrderId(SeqTools.dateSeq());
        partnerInfo.setUserId(0L);
        partnerInfo.setStatus("1");
        partnerInfo.setPartnerCode(req.getPartnerCode());
        partnerInfo.setPartnerTitle(goods.getTitle());
        partnerInfo.setPartnerUserId(partnerUser.getId());
        partnerInfo.setPartnerTypeId(Long.valueOf(goods.getTypeId()));
        partnerInfo.setPrice(goods.getPrice());
        partnerInfo.setPayMoney(BigDecimal.ZERO);
        partnerInfo.setUserName(req.getUserName());
        partnerInfo.setTelephone(partnerUser.getPhone());
        partnerInfo.setAddressDetail(req.getAddressDetail());
        partnerInfo.setCreateBy(LoginUserHolder.getAdminId());
        partnerInfo.setUpdateBy(LoginUserHolder.getAdminId());
        orderPartnerInfoRepository.save(partnerInfo);
    }

    private PromotePointRecord insertRecord(Long userId, String type, PPointScene scene, BigDecimal point, OrderPartnerInfo partnerInfo) {
        String seq = SeqTools.dateSeq();
        PromotePointRecord record = new PromotePointRecord();
        record.setPromoteSeqNo(seq);
        record.setRecordType(type);
        record.setPointAmount(point);
        record.setUserId(userId);
        record.setSceneCode(scene.getCode());

        record.setBusinessId(partnerInfo.getOrderId());
        record.setItemId(String.valueOf(partnerInfo.getPartnerCode()));
        record.setItemName(partnerInfo.getPartnerTitle());

        record.setCreateBy(partnerInfo.getCreateBy());
        record.setUpdateBy(partnerInfo.getCreateBy());

        promotePointRecordRepository.save(record);
        log.info("插入活动积分记录表记录: {}", JsonUtils.toJsonString(record));
        return record;
    }

    private Specification<OrderPartnerInfo> buildCondition(Long userId, Long partnerCode, String userName, String telephone, Long partnerUserId, Long partnerTypeId) {
        return buildCondition(userId, partnerCode, userName, telephone, partnerUserId, partnerTypeId, "1");
    }

    /**
     * 构建查询条件
     * @param userId
     * @param partnerCode
     * @param userName
     * @param telephone
     * @return
     */
    private Specification<OrderPartnerInfo> buildCondition(Long userId, Long partnerCode, String userName, String telephone, Long partnerUserId, Long partnerTypeId, String status) {

        log.info("查询合伙人信息，查询条件: userId={}, partnerCode = {}, userName = {}, telephone = {}", userId, partnerCode, userName, telephone);

        Specification<OrderPartnerInfo> queryCondition = (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = Lists.newArrayList();
            if(userId != null) {
                predicates.add(criteriaBuilder.equal(root.get("userId"), userId));
            }
            if(partnerUserId != null) {
                predicates.add(criteriaBuilder.equal(root.get("partnerUserId"), partnerUserId));
            }
            if(partnerTypeId != null) {
                predicates.add(criteriaBuilder.equal(root.get("partnerTypeId"), partnerTypeId));
            }
            if(partnerCode != null) {
                predicates.add(criteriaBuilder.equal(root.get("partnerCode"), partnerCode));
            }
            if(StringUtils.isNotEmpty(userName)) {
                predicates.add(criteriaBuilder.like(root.get("userName"), "%" + userName + "%"));
            }
            if(StringUtils.isNotEmpty(telephone)) {
                predicates.add(criteriaBuilder.equal(root.get("telephone"), telephone));
            }
            if(StringUtils.isNotEmpty(status)) {
                predicates.add(criteriaBuilder.equal(root.get("status"), status));
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        return queryCondition;
    }
}
